﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;

namespace Common
{

    abstract public class FlagBase<TValue>
    {
        protected TValue value;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="value"></param>
        protected FlagBase(TValue value)
        {
            this.value = value;
        }

        /// <summary>
        /// 隐式类型转换
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static implicit operator TValue(FlagBase<TValue> value)
        {
            if (value == null)
                return default(TValue);
            else
                return value.value;
        }


        /// <summary>
        /// 重写ToString方法
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return value.ToString();
        }

        public TValue Value
        {
            get { return value; }
        }
    }
  
    abstract public class StrictFlagBase<TValue, TFlag> : FlagBase<TValue>
        where TFlag : FlagBase<TValue>
    {
        private static object _syncObj = new object();
        protected static ConcurrentDictionary<TValue, TFlag> _allInstances;

        protected StrictFlagBase(TValue value) : base(value) { }

        public static bool TryConvert(TValue value, out TFlag flag)
        {
            EnsureInited();
            TFlag rtn;
            bool exsits = _allInstances.TryGetValue(value, out rtn);

            if (exsits)
            {
                flag = rtn;
                return true;
            }
            else
            {
                flag = default(TFlag);
                return false;
            }
        }

        private static void EnsureInited()
        {
            if (_allInstances == null)
            {
                lock (_syncObj)
                {
                    if (_allInstances == null)
                    {
                        _allInstances = new ConcurrentDictionary<TValue, TFlag>();
                        var allFields = typeof(TFlag).GetFields();
                        foreach (var field in allFields)
                        {
                            if (field.FieldType == typeof(TFlag) && field.IsPublic && field.IsStatic && field.IsInitOnly)
                            {
                                TFlag f = field.GetValue(null) as TFlag;
                                _allInstances.TryAdd(f.Value, f);
                            }
                        }
                    }
                }
            }
        }

    }

    abstract public class FlexibleFlagBase<TValue, TFlag> : FlagBase<TValue>
        where TFlag : FlagBase<TValue>
    {
        private static object _syncObj = new object();
        protected static ConcurrentDictionary<TValue, TFlag> _allInstances;

        protected FlexibleFlagBase(TValue value) : base(value) { }

        public static bool TryConvert(TValue value, out TFlag flag)
        {
            EnsureInited();
            flag = _allInstances.GetOrAdd(value, ConstructInstance);
            return true;
        }

        private static TFlag ConstructInstance(TValue value)
        {
            ObjectCreator<TFlag> oc = ObjectCreator<TFlag>.Instance;
            var newFlag = oc.Create(new Type[] { typeof(TValue) }, value);
            return newFlag;
        }

        private static void EnsureInited()
        {
            if (_allInstances == null)
            {
                lock (_syncObj)
                {
                    if (_allInstances == null)
                    {
                        _allInstances = new ConcurrentDictionary<TValue, TFlag>();
                        var allFields = typeof(TFlag).GetFields();
                        foreach (var field in allFields)
                        {
                            if (field.FieldType == typeof(TFlag) && field.IsPublic && field.IsStatic && field.IsInitOnly)
                            {
                                TFlag f = field.GetValue(null) as TFlag;
                                _allInstances.TryAdd(f.Value, f);
                            }
                        }
                    }
                }
            }
        }
      
    }

}
